﻿using System;

namespace H.Types
{
    /// <summary>
    /// 表示一个矩形
    /// </summary>
    [Serializable]
    public struct Rect
    {
        /// <summary>
        /// 表示一个空的区域其值为0,0,0,0
        /// </summary>
        public static readonly Rect Empty;

        /// <summary>
        /// 矩形的左上角的横坐标
        /// </summary>
        public int X { get; set; }

        /// <summary>
        /// 矩形的左上角的纵坐标
        /// </summary>
        public int Y { get; set; }

        /// <summary>
        /// 矩形的宽度
        /// </summary>
        public int W { get; set; }

        /// <summary>
        /// 矩形的高度
        /// </summary>
        public int H { get; set; }

        /// <summary>
        /// 矩形左边坐标
        /// </summary>
        public int Left => X;

        /// <summary>
        /// 矩形右边坐标
        /// </summary>
        public int Right => X + W;

        /// <summary>
        /// 矩形顶部坐标
        /// </summary>
        public int Top => Y;

        /// <summary>
        /// 矩形底部坐标
        /// </summary>
        public int Bottom => Y + H;

        /// <summary>
        /// 矩形左上角坐标
        /// </summary>
        public Pos LeftTop => new Pos(X, Y);

        /// <summary>
        /// 矩形右上角坐标
        /// </summary>
        public Pos RightTop => new Pos(X + W, Y);

        /// <summary>
        /// 矩形左下角坐标
        /// </summary>
        public Pos LeftBottom => new Pos(X, Y + H);

        /// <summary>
        /// 矩形右上角坐标
        /// </summary>
        public Pos RightBottom => new Pos(X + W, Y + H);


        /// <summary>
        /// 区域是否为空
        /// </summary>
        public bool IsEmpty => X == 0 && Y == 0 && W == 0 && H == 0;

        /// <summary>
        /// 构造一个矩形
        /// </summary>
        /// <param name="x">左上角横坐标</param>
        /// <param name="y">左上角纵坐标</param>
        /// <param name="w">矩形宽度</param>
        /// <param name="h">矩形高度</param>
        public Rect(int x, int y, int w, int h)
        {
            X = x;
            Y = y;
            W = w;
            H = h;
        }

        public Rect(Pos pos, int w, int h)
        {
            X = pos.X;
            Y = pos.Y;
            W = w;
            H = h;
        }

        /// <summary>
        /// 通过左上角顶点和右下角顶点确定一个矩形
        /// </summary>
        /// <param name="l">左上角顶点横坐标</param>
        /// <param name="t">左上角顶点纵坐标</param>
        /// <param name="r">右下角顶点横坐标</param>
        /// <param name="b">右下角顶点纵坐标</param>
        /// <returns>此函数生成的矩形结构</returns>
        public static Rect FromLTRB(int l, int t, int r, int b)
        {
            Rect rect = new Rect
            {
                X = l < r ? l : r,
                Y = t < b ? t : b,
                W = Math.Abs(l - r),
                H = Math.Abs(t - b)
            };
            return rect;
        }

        /// <summary>
        /// 检查此矩形和点是否相交
        /// </summary>
        /// <param name="point"></param>
        /// <returns>如果点在矩形内,返回true</returns>
        public bool Intersect(Pos point)
        {
            return Left <= point.X && point.X <= Right && Top <= point.Y && point.Y <= Bottom;
        }

        /// <summary>
        /// 检查此矩形和点是否相交
        /// </summary>
        /// <param name="x">测试点X</param>
        /// <param name="y">测试点Y</param>
        /// <returns>如果点在矩形内,返回true</returns>
        public bool Intersect(int x, int y)
        {
            return Left <= x && x <= Right && Top <= y && y <= Bottom;
        }


        /// <summary>
        /// 用于产生随机点的随机数产生器
        /// </summary>
        private static readonly Random _Random = new Random();

        /// <summary>
        /// 在矩形内任意取一个点
        /// </summary>
        /// <returns></returns>
        public Pos RandomPoint()
        {
            return new Pos(_Random.Next(Left, Right), _Random.Next(Top, Bottom));
        }

        /// <summary>
        /// 偏移当前矩形
        /// </summary>
        /// <param name="offset"></param>
        public void Offset(Pos offset)
        {
            X += offset.X;
            Y += offset.Y;
        }

        /// <summary>
        /// 偏移当前矩形
        /// </summary>
        /// <param name="x">希望加上的偏移量X</param>
        /// <param name="y">希望加上的偏移量Y</param>
        public void Offset(int x, int y)
        {
            X += x;
            Y += y;
        }

        /// <summary>
        /// 将当前的Rect加上指定的偏移,并生成新的偏移
        /// </summary>
        /// <param name="x">希望加上的偏移量X分量</param>
        /// <param name="y">希望加上的偏移量Y分量</param>
        /// <returns>返回一个新的Rect结构</returns>
        public Rect Add(int x, int y)
        {
            return new Rect(X + x, Y + y, W, H);
        }

        /// <summary>
        /// 将当前的Rect加上指定的偏移,并生成新的偏移
        /// </summary>
        /// <param name="offset">希望加上的偏移量</param>
        /// <returns>返回一个新的Rect结构</returns>
        public Rect Add(Pos offset)
        {
            return new Rect(X + offset.X, Y + offset.Y, W, H);
        }

        /// <summary>
        /// 将当前的Rect减去指定的偏移,并生成新的偏移
        /// </summary>
        /// <param name="x">希望减去的偏移量X分量</param>
        /// <param name="y">希望减去的偏移量Y分量</param>
        /// <returns>返回一个新的Rect结构</returns>
        public Rect Sub(int x, int y)
        {
            return new Rect(X - x, Y - y, W, H);
        }

        /// <summary>
        /// 将当前的Rect减去指定的偏移,并生成新的偏移
        /// </summary>
        /// <param name="offset">希望减去的偏移量</param>
        /// <returns>返回一个新的Rect结构</returns>
        public Rect Sub(Pos offset)
        {
            return new Rect(X - offset.X, Y - offset.Y, W, H);
        }

        /// <summary>
        /// 将Rect表示为X,Y,W,H形式的字符串
        /// </summary>
        /// <returns>返回转换后的字符串</returns>
        public string MakeXYWH()
        {
            return $"{X},{Y},{W},{H}";
        }

        /// <summary>
        /// 将Rect表示为一个L,T,R,B形式的字符串
        /// </summary>
        /// <returns>返回转换后的字符串</returns>
        public string MakeLTRB()
        {
            return $"{Left},{Top},{Right},{Bottom}";
        }

        public override string ToString()
        {
            return $"{{Rect:{Left},{Top},{Right},{Bottom}({W},{H})}}";
        }

        public override bool Equals(object obj)
        {
            if (obj is Rect rect)
            {
                return rect.X == X && rect.Y == Y && rect.W == W && rect.H == H;
            }
            else
            {
                return false;
            }
        }

        public override int GetHashCode()
        {
            return X ^ Y ^ W ^ H;
        }
    }
}
